package com.gitee.linmt.config;

import com.gitee.linmt.entity.Permission;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.method.AbstractMethodSecurityMetadataSource;
import org.springframework.security.access.prepost.PostInvocationAttribute;
import org.springframework.security.access.prepost.PreInvocationAttribute;
import org.springframework.security.access.prepost.PrePostInvocationAttributeFactory;
import org.springframework.stereotype.Controller;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;

import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;

/**
 * 从数据库获取权限配置的数据源.
 *
 * @author <a href="mailto:lin-mt@outlook.com">lin-mt</a>
 */
public class PrePostJdbcSecurityMetadataSource extends AbstractMethodSecurityMetadataSource {
    
    private final PrePostInvocationAttributeFactory attributeFactory;
    
    private final AntPathMatcher pathMatcher = new AntPathMatcher();
    
    public PrePostJdbcSecurityMetadataSource(PrePostInvocationAttributeFactory attributeFactory) {
        this.attributeFactory = attributeFactory;
    }
    
    protected boolean isHandler(Class<?> beanType) {
        return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) || AnnotatedElementUtils
                .hasAnnotation(beanType, RequestMapping.class));
    }
    
    private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
        RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
        return (requestMapping != null ? createRequestMappingInfo(requestMapping) : null);
    }
    
    protected RequestMappingInfo createRequestMappingInfo(RequestMapping requestMapping) {
        RequestMappingInfo.Builder builder = RequestMappingInfo.paths(requestMapping.path());
        return builder.build();
    }
    
    protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
        RequestMappingInfo info = createRequestMappingInfo(method);
        if (info != null) {
            RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
            if (typeInfo != null) {
                info = typeInfo.combine(info);
            }
        }
        return info;
    }
    
    @Override
    public Collection<ConfigAttribute> getAttributes(Method method, Class<?> targetClass) {
        if (method.getDeclaringClass() == Object.class || !isHandler(targetClass)) {
            return Collections.emptyList();
        }
        RequestMappingInfo mappingForMethod = getMappingForMethod(method, targetClass);
        if (mappingForMethod == null) {
            return Collections.emptyList();
        }
        Permission permission = new Permission();
        permission.setUrlPattern("/app/**");
        ArrayList<ConfigAttribute> attrs = new ArrayList<>(2);
        for (String pattern : mappingForMethod.getPatternsCondition().getPatterns()) {
            if (!pathMatcher.match(permission.getUrlPattern(), pattern)) {
                continue;
            }
            System.err.println(pattern);
            String preFilterAttribute = null;
            String filterObject = null;
            String preAuthorizeAttribute = null;
            String postFilterAttribute = null;
            String postAuthorizeAttribute = null;
            PreInvocationAttribute pre = attributeFactory
                    .createPreInvocationAttribute(preFilterAttribute, filterObject, preAuthorizeAttribute);
            if (pre != null) {
                attrs.add(pre);
            }
            
            PostInvocationAttribute post = attributeFactory
                    .createPostInvocationAttribute(postFilterAttribute, postAuthorizeAttribute);
            if (post != null) {
                attrs.add(post);
            }
        }
        attrs.trimToSize();
        return attrs;
    }
    
    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {
        return null;
    }
    
}

